#include "MyTcpServer.h"

ProcessConnectCallBack g_CallBackRun;//回调函数


/*
服务器流程
1.创建套接字（socket）-->2.绑定端口（bind）-->3.监听（listen）-->4.接受（accept）-->5.接收（rece）或者发送（send）消息-->6.关闭（close）

客户端流程
1.创建套接字（socket）-->2.连接服务器（connect）-->3.接收（rece）或者发送（send）消息-->4.关闭（close）
*/

SocketServer::SocketServer(int port)
{
   m_nPort = port;
}

SocketServer::~SocketServer()
{
   google::ShutdownGoogleLogging();     // 停止GLog
}

/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
 bool SocketServer::m_bCreatSocket()
 {

    if((m_nSockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  //创建套接字，确定协议类型SOCK_STREAM表示TCP流式套接口  SOCK_GDRAM表示UDP数据报套接口
     {
       fprintf(stderr, "socket error: %s\n", strerror(errno));
       LOG(ERROR) << "socket error:" << strerror(errno);                  //日志
       return false;
     }

//       int TimeOut=6000; //设置发送超时6秒
//        if(::setsockopt(m_nSockFd,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
//        {
//           fprintf(stderr, "设置发送超时 error: %s\n", strerror(errno));
//           LOG(ERROR) << "设置发送超时 error:" << strerror(errno);
//           return false;
//        }
//        TimeOut=6000;//设置接收超时6秒
//        if(::setsockopt(m_nSockFd,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
//        {
//           fprintf(stderr, "设置接收超时 error: %s\n", strerror(errno));
//           LOG(ERROR) << "设置接收超时 error:" << strerror(errno);
//           return false;
//       }
//struct timeval timeout={4,0};
//即timeout={4,0};或者timeout.tv_sec=4; timeout.tv_usec=0;
//设置接收超时
//setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
//setsockopt(m_nSockFd,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval));


       return true;
 }

/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
bool SocketServer::m_bBindPort()
{
     /*填充sockaddr结构*/
     bzero(&s_addr, sizeof(s_addr));
     s_addr.sin_family = AF_INET;  //固定为AF_INET
     s_addr.sin_port = htons(m_nPort);  //端口
     s_addr.sin_addr.s_addr = htonl(INADDR_ANY); //系统指定的IP地址
     /*绑定套接字与描述符*/
     if(bind(m_nSockFd, (struct sockaddr*)&s_addr, sizeof(s_addr)) == -1)  //指定IP地址和端口信息  存在sockaddr_in里
     {
      fprintf(stderr, "bind error: %s\n", strerror(errno));
      LOG(ERROR) << "bind error:" << strerror(errno);                  //日志
      return false;
     }
     return true;
    }
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
bool SocketServer::m_bListen()
{
         /*监听sockfd描述符*/
     if(listen(m_nSockFd, 1000) == -1)  //指定等待连接的最大队列长度是10
     {
      fprintf(stderr, "listen error: %s", strerror(errno));
      LOG(ERROR) << "listen error:" << strerror(errno);                  //日志
      return false;
     }
     printf(" listen...\n");
    /*
    fcntl和select是了两种解决I/O 多路复用的解决方法

    1．fcntl
    函数fcntl针对socket编程提供了如下的编程特性。
    · 非阻塞I/O：可将cmd设置为F_SETFL，将lock设置为O_NONBLOCK。
    · 信号驱动I/O：可将cmd设置为F_SETFL，将lock设置为O_ASYNC。
    */
    /*调用fcntl函数设置非阻塞参数*/
        int flags;

        if((flags = fcntl(m_nSockFd,F_GETFL,0))==-1)
        {
                perror("fcntl F_GETFL fail:");
                LOG(ERROR) << "fcntl F_GETFL fail:";
                return false;
        }
        flags |= O_ASYNC;
        if(fcntl(m_nSockFd,F_SETFL,flags)==-1)
        {
                perror("fcntl F_SETFL fail:");
                LOG(ERROR) << "fcntl F_SETFL fail:";
                return false;
        }
        return true;
  }

/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
int SocketServer::m_bAccept()
    {
      int new_fd = -1;
      m_nSize = sizeof(struct sockaddr);
      /*阻塞服务器，获取连接*/
      printf(" accept...\n");
/*****************************************4.接受（accept）**********************************************/
      if((new_fd = accept(m_nSockFd, (struct sockaddr*)&c_addr, (socklen_t*)&m_nSize)) < 0)  //注意：阻塞模式
      {
       fprintf(stderr, "accept error: %s\n", strerror(errno));
       return -1;
      }
      fprintf(stderr, "m_nNewfd = %d\n", new_fd);
      LOG(INFO) << "connnect fd = " << new_fd;
/*********************************************************************/

      return new_fd;
    }
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
 void Perr(int signum)
{
    if(signum==SIGPIPE)
    {
      fprintf(stderr, "Port Error: %s\n", strerror(errno));
       LOG(ERROR) << "Port Error: " << strerror(errno);
    }
}
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
void SocketServer::Init()
{
    mkdir("./Log",S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);
    mkdir("./Log/loginfo",S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);
    mkdir("./Log/logwarn",S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);
    mkdir("./Log/logerr",S_IRUSR | S_IWUSR | S_IXUSR | S_IRWXG | S_IRWXO);

    google::InitGoogleLogging("ServerLog"); // 设置日志文件名中的"文件名"字段.

    google::SetLogDestination(google::INFO,"./Log/loginfo/");
    google::SetLogDestination(google::WARNING,"./Log/logwarn/");
    google::SetLogDestination(google::GLOG_ERROR,"./Log/logerr/");
    FLAGS_logtostderr = false;

    signal(SIGPIPE,Perr);

    LOG(INFO) << "启动服务器";           //日志
}
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
bool SocketServer::Start()
{
            if(!m_bCreatSocket())
            {
                LOG(ERROR) << "创建socket失败";            //日志
                google::ShutdownGoogleLogging();          // 停止GLog
                return false;
            }
            if(!m_bBindPort())
            {
                LOG(ERROR) << "绑定端口失败";               //日志
                google::ShutdownGoogleLogging();          // 停止GLog
                return false;
            }
            if(!m_bListen())
            {
                LOG(ERROR) << "监听失败";                  //日志
                google::ShutdownGoogleLogging();          // 停止GLog
                return false;
            }

            pthread_t Pid;
              if(pthread_create(&Pid,NULL,&ThreadGetNewConnect,this))
              {
                 LOG(ERROR) << "监听新连接线程失败";          //日志
                 google::ShutdownGoogleLogging();          // 停止GLog
                 return false;
              }
}
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
void SocketServer::SetProcessConnectCallBack(ProcessConnectCallBack pRun)
{
  g_CallBackRun = pRun;
}
/****************************
Function   :
Parameter  :
ReturnValue:
Author     :
Data       :
****************************/
void *ThreadGetNewConnect(void*p)
{

  SocketServer *pServer = (SocketServer*)p;
  pServer->GetNewConnect();

}
void SocketServer::GetNewConnect()
{
   while(1)
            {
                  m_nNewfd = m_bAccept();
                  if(m_nNewfd < 0 )
                  {
                     LOG(ERROR) << "建立连接失败";                  //日志
                     continue;
                  }
                  if(g_CallBackRun == NULL)
                  {
                     continue;
                  }
                  int new_fd = m_nNewfd;
                  pthread_t Pid;
                  if(pthread_create(&Pid,NULL,ThreadCallBack,&new_fd))
                  {
                     LOG(ERROR) << "创建线程失败";                  //日志
                     continue;
                  }
            }
}
void *ThreadCallBack(void*p)
{
    int *fd = (int*)p;
    (*g_CallBackRun)(*fd);
}

